#include "typedef.h"
#include "asm.h"
#include "stdio.h"
#include "kbd.h"
#include "interrupts.h"
#include "dev.h"
#include "pic.h"
#include "vga.h"

u8 lowercase[256] = { 0,  27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 
   '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[',
   ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 
    0,  '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ',
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0
};

u8 uppercase[256] = { 0,  27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
   '_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{',
   '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '"', 0,
   '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0
};


static u8 flags;
static u8* kbd_buf;
static int buf_pos, max_read;
//static queue_t kbd_data;


void init_kbd() {
  register_isr(KBD_IRQ, kbd_isr);
  // init_queue(&kbd_data, &kbd_buf[0], 10);
  flags = 0;
  flags |= KBD_ECHO;
  enable_irq(KEYBOARD);
}

int kbd_read(u8* buf, int count) {
  cli();
  kbd_buf = buf;
  buf_pos = 0;
  max_read = count;
  flags |= KBD_READ;
  sti();

  while(buf_pos != count)
    ;

  return count;
}
  
void kbd_isr() { 
  u8 ch = inb(0x60);
  u8 ascii_char;
 
  if(ch == 0xE0)
    ch = inb(0x60);
 
  // check shift state
  if(ch == KB_LSHIFT || ch == KB_RSHIFT) {
      flags |= KBD_SHIFT;
      return;
  }
    
  else if(ch == (KB_LSHIFT + KEY_RELEASE_N) ||
	  ch == (KB_RSHIFT + KEY_RELEASE_N)) {
      flags &= ~KBD_SHIFT;
      return;
  }

  /* break codes a regular codes + 0x80. If this
     is a break code, just ignore */
  else if((ch - KEY_RELEASE_N) > 0)
    return;

  // if caps lock press of release, invert switch state
  else if(ch ==  KB_CAPSLOCK) {

      if(flags & KBD_SHIFT)
        flags &= ~KBD_SHIFT;
      else
        flags |= KBD_SHIFT;

      return;
  }

  else if(ch == KB_DEL) {
    putc(' ');
    update_cursor(-1, 0);
    return;
  }
  
  else if(ch == KB_DOWN) {
    update_cursor(0, -1);
    return;
  }

  else if(ch == KB_UP) {
    update_cursor(0, 1);
    return;
  }

  else if(ch == KB_LEFT) {
    update_cursor(-1, 0);
    return;
  }

  else if(ch == KB_RIGHT) {
    update_cursor(1, 0);
    return;
  }

  else {
    ascii_char = ((flags & KBD_SHIFT) ? uppercase[ch] : lowercase[ch]);

    // if someone is reading
    if(flags & KBD_READ) {
      if(ascii_char >= 0x20 && ascii_char <= 0x7E) {
	*(kbd_buf + buf_pos) = ascii_char;
	buf_pos++;

        if(buf_pos == max_read)
	  flags &= ~KBD_READ;
      }  
    }

    if(flags & KBD_ECHO) 
      putc(ascii_char);
      
    return;
  }
}

